home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / db / esm-3.1 / esm-3 / usr / local / sm / src / serverlib / msg / timer.C < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-05  |  11.1 KB  |  479 lines

  1. /*
  2.  *   $RCSfile: timer.C,v $  
  3.  *   $Revision: 1.1.1.1 $  
  4.  *   $Date: 1996/05/04 21:55:54 $      
  5.  */ 
  6. /**********************************************************************
  7. * EXODUS Database Toolkit Software
  8. * Copyright (c) 1991 Computer Sciences Department, University of
  9. *                    Wisconsin -- Madison
  10. * All Rights Reserved.
  11. *
  12. * Permission to use, copy, modify and distribute this software and its
  13. * documentation is hereby granted, provided that both the copyright
  14. * notice and this permission notice appear in all copies of the
  15. * software, derivative works or modified versions, and any portions
  16. * thereof, and that both notices appear in supporting documentation.
  17. *
  18. * THE COMPUTER SCIENCES DEPARTMENT OF THE UNIVERSITY OF WISCONSIN --
  19. * MADISON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION.  
  20. * THE DEPARTMENT DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES
  21. * WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
  22. *
  23. * The EXODUS Project Group requests users of this software to return 
  24. * any improvements or extensions that they make to:
  25. *
  26. *   EXODUS Project Group 
  27. *     c/o David J. DeWitt and Michael J. Carey
  28. *   Computer Sciences Department
  29. *   University of Wisconsin -- Madison
  30. *   Madison, WI 53706
  31. *
  32. *     or exodus@cs.wisc.edu
  33. *
  34. * In addition, the EXODUS Project Group requests that users grant the 
  35. * Computer Sciences Department rights to redistribute these changes.
  36. **********************************************************************/
  37. #include "ess.h"
  38. #include "checking.h"
  39. #include "sysdefs.h"
  40. #include "trace.h"
  41. #include "error.h"
  42. #include "list.h"
  43. #include "timer.h"
  44. #include "lock.h"
  45. #include "tid.h"
  46. #include "thread.h"
  47. #include "threadstate.h"
  48. #include "thread_globals.h"
  49. #include "thread_funcs.h"     /* TODO: remove this line */
  50.  
  51. Timer ServerTimer; 
  52. static int ngettimeofday = 0;
  53.  
  54. /*
  55.  * similar to <sys/callout.h> but uses our list functions
  56.  */
  57. typedef struct  callout {
  58.     LISTELEMENT      list;
  59.     TIME             timeLeft;   /* incremental time */
  60.     TIMERFUNC        func;
  61.     TIMERARG        arg;          /* argument to routine */
  62. #ifdef DEBUG
  63.     int                cardinal;    /* nth callout started */
  64. #endif DEBUG
  65. } CALLOUT;
  66.  
  67.  
  68. /* ******************************* CLOCK ******************************** */
  69.  
  70. TIME
  71. Clock:: Elapsed(BOOL reset)
  72. {
  73.     SELECTTIME         currentTime;
  74.     register TIME     result;
  75.     struct timezone timeZone;
  76.  
  77.     if(gettimeofday( ¤tTime, &timeZone) < 0) {
  78.         SM_ERROR(TYPE_FATAL, errno);
  79.     }
  80.     ngettimeofday++;
  81.     result = (TIME)(currentTime.tv_sec - timeOfLastTick.tv_sec) ;
  82.  
  83.     TRPRINT(TR_TEST, TR_LEVEL_1, 
  84.         ("CLOCK::Elapsed: @ %x, %d sec since last reset", 
  85.         currentTime.tv_sec, result));
  86.  
  87.     if(reset)
  88.         timeOfLastTick = currentTime;
  89.     return result;
  90. }
  91.  
  92. TIME
  93. Clock::LastSleepTime()
  94. {
  95.     return (TIME)(timeOfLastTick.tv_sec);
  96. }
  97.  
  98. void
  99. Clock:: printStats()
  100. {
  101.     fprintf(stdout, "Clock:\t last tick @ 0x%x (%d sec ago)\n",
  102.         timeOfLastTick.tv_sec, Elapsed());
  103. }
  104.  
  105. void
  106. Clock:: clearStats()
  107. {
  108. }
  109.  
  110. #ifdef DEBUG
  111. void
  112. Clock:: dump()
  113. {
  114.     printStats();
  115. }
  116. #endif DEBUG
  117.  
  118. /* ******************************* TIMER ******************************** */
  119.  
  120. #define enterTIMER \
  121.     if(Active != NULL) {\
  122.         if(Active->state == THREAD_TIMER_IN_USE) {\
  123.             SM_ERROR(TYPE_FATAL, esmINTERNAL);\
  124.         }\
  125.         Active->state = THREAD_TIMER_IN_USE; \
  126.     }
  127.  
  128. void
  129. Timer:: Init( int maxTimers )
  130.     /* 
  131.      * Not a constructor because we want to call it
  132.      * after we get the options from the command line.
  133.      */
  134. {
  135.     CALLOUT *freeHeap;
  136.  
  137.     ncallout=0;
  138.     INITIALIZELIST(&expiredList);
  139.     INITIALIZELIST(&calloutList);
  140.     INITIALIZELIST(&freeList);
  141.  
  142.     freeHeap = (CALLOUT *)calloc(maxTimers, sizeof(CALLOUT));
  143.     for( nfree = 0; nfree < maxTimers; nfree++ ) {
  144.         INITIALIZELISTELEMENT(freeHeap, &(freeHeap->list));
  145.         listEnq( &freeList, &(freeHeap->list) );
  146.         freeHeap++;
  147.     }
  148.  
  149.     nstarted = ncalled = nremoved = nnotfound = 0;
  150.  
  151.     highwaterUsed = 0;
  152. }
  153.  
  154. TIME 
  155. Timer:: Tick( 
  156.     BOOL forceResetClock, 
  157.     BOOL checkForExpired 
  158. )
  159. {
  160.     TIME        elapsedSec;
  161.     TIME        overExpired;
  162.     CALLOUT        *firstCallout;
  163.  
  164.  
  165.     TRPRINT(TR_TEST, TR_LEVEL_1, ("Tick (%d,%d)", forceResetClock,
  166.         checkForExpired));
  167.  
  168. #ifdef DEBUG
  169.     firstCallout = (CALLOUT *) FIRST_LIST_ELEMENT(&calloutList);
  170.     if(ncallout > 0) {
  171.         SM_ASSERT(LEVEL_1, firstCallout != NULL);
  172.     } else {
  173.         SM_ASSERT(LEVEL_1, firstCallout == NULL);
  174.     }
  175. #endif DEBUG
  176.  
  177.     if(ncallout <= 0 )  {
  178.         if(forceResetClock) Reset;
  179.         goto Done;
  180.     }
  181.  
  182.     nticks++;
  183.  
  184.     elapsedSec = Reset;   /* now we MUST update the first element */
  185.  
  186.     TRPRINT(TR_TEST, TR_LEVEL_1, ("tick %d secs", elapsedSec));
  187.  
  188.     firstCallout = (CALLOUT *) FIRST_LIST_ELEMENT(&calloutList);
  189.     firstCallout->timeLeft -= elapsedSec;
  190.  
  191.     if( !checkForExpired )
  192.         goto Done;
  193.  
  194.     while ((firstCallout != NULL) && (firstCallout->timeLeft <= 0)) {
  195.         overExpired = 0-firstCallout->timeLeft;
  196.  
  197.         /* 
  198.          * Remove the callout from the timer list
  199.          * and put it on the expired list
  200.          */
  201.         LISTREMOVE(&(firstCallout->list));
  202.         ncallout --;
  203.         LISTENQ(&expiredList, &(firstCallout->list));
  204.         nexpired++;
  205.  
  206.         TRPRINT(TR_TEST, TR_LEVEL_1, 
  207.         ("Expired #%d, f=0x%x(a=0x%x)", 
  208.             firstCallout->cardinal, firstCallout->func, firstCallout->arg));
  209.  
  210.         if(    (firstCallout = 
  211.             (CALLOUT *) FIRST_LIST_ELEMENT(&calloutList)) != NULL) {
  212.             firstCallout->timeLeft -= overExpired;
  213.         }
  214.     } 
  215.  
  216.     if(firstCallout)
  217.         SM_ASSERT(LEVEL_1, firstCallout->timeLeft > 0);
  218.  
  219.     enterTIMER;
  220.     
  221.     /* 
  222.      * Now, call all the funcs in the expired list
  223.      */
  224.     while  (nexpired > 0) {
  225.  
  226.         SM_ASSERT(LEVEL_1, LIST_NOT_EMPTY(&expiredList));
  227.  
  228.         ncalled++;
  229.  
  230.         firstCallout = (CALLOUT *)listDeq(&expiredList);
  231.         nexpired --;
  232.  
  233.         (*(firstCallout->func))(firstCallout->arg);
  234.  
  235.         listEnq(&freeList, &(firstCallout->list));
  236.         nfree++;
  237.     }
  238.     Active->state = THREAD_ACTIVE; 
  239.     if(    ncallout > 0) {
  240.         firstCallout = (CALLOUT *) FIRST_LIST_ELEMENT(&calloutList) ;
  241.         SM_ASSERT(LEVEL_1, firstCallout->timeLeft > 0);
  242.     }
  243.  
  244. Done:
  245.     SM_ASSERT(LEVEL_1, ncallout >= 0); /* sanity */
  246.     if(ncallout != 0) {
  247.  
  248. #ifdef DEBUG
  249.         if( firstCallout->timeLeft <  0) {
  250.             SM_ASSERT(LEVEL_1,  checkForExpired == FALSE);
  251.         }
  252. #endif DEBUG
  253.  
  254.         TRPRINT(TR_TEST, TR_LEVEL_1, 
  255.             ("Tick returns %d for callout #%d", firstCallout->timeLeft,
  256.                 firstCallout->cardinal));
  257.         return firstCallout->timeLeft;
  258.     } else {
  259.         TRPRINT(TR_TEST, TR_LEVEL_1, ("Tick returns 0"));
  260.         return  0;
  261.     }
  262.     /*NOTREACHED*/
  263. }
  264.  
  265. void
  266. Timer:: Start(
  267.     TIME         newTime, 
  268.     TIMERFUNC     newFunc, 
  269.     TIMERARG     newArg
  270. )
  271. {
  272.     CALLOUT        *newCallout, *thisCallout;
  273.  
  274.     enterTIMER;
  275.  
  276.     TRACE(TR_TEST, TR_LEVEL_1);
  277.     /* 
  278.      * sanity check:
  279.      * No sense in zero timers -may want to change this later.
  280.      * No sense in timers > a week.
  281.      */
  282.     SM_ASSERT(LEVEL_1, (newTime > 0) ); 
  283.  
  284. #define MINUTE 60
  285. #define HOUR    (60*MINUTE)
  286. #define DAY        (24*HOUR)
  287. #define WEEK    (7*DAY)
  288.     SM_ASSERT(LEVEL_1, (newTime <= WEEK) ); 
  289.  
  290.  
  291.     /* 
  292.      * First, reset the clock so that it reflects "now",
  293.      * and update the first element in the list (if it exists)
  294.      * so that the new callout and the existing list of callouts
  295.      * are relative to the same base time.
  296.      */
  297.  
  298.  
  299.     (void) Tick(TRUE     /* reset the clock even if no items on the list */,
  300.                 FALSE     /* do not check for expired timeouts */    );
  301.  
  302.     /* 
  303.      * Now get a new callout.
  304.      */
  305.     if(nfree <= 0) {
  306.         SM_ERROR(TYPE_FATAL, esmNOFREETIMERS);
  307.     }
  308.     nstarted++;
  309.     newCallout = (CALLOUT *)listDeq(&freeList);
  310.     nfree--;
  311.     highwaterUsed = (highwaterUsed < ++ncallout) ? ncallout : highwaterUsed;
  312.  
  313.     TRPRINT(TR_TEST, TR_LEVEL_1, 
  314.         ("starting func 0x%x(arg 0x%x) with time %d", newFunc, newArg, newTime));
  315.     newCallout->timeLeft =  newTime;
  316.     SM_ASSERT(LEVEL_1, newCallout->timeLeft >= 0);
  317.     newCallout->func     =  newFunc;
  318.     newCallout->arg         =  newArg;
  319.  
  320. #ifdef DEBUG
  321.     newCallout->cardinal  =  nstarted;
  322. #endif DEBUG
  323.  
  324.  
  325.     /* 
  326.      * Put the timer in the list. The clock reflects "now", so all the
  327.      * relative times are legit wrt "now", which means that some may be
  328.      * negative.
  329.      */
  330.     for(
  331.         thisCallout = (CALLOUT *) FIRST_LIST_ELEMENT(&calloutList);
  332.         thisCallout != NULL;
  333.         thisCallout = (CALLOUT *)NEXT_LIST_ELEMENT( &(thisCallout->list) ) ) {
  334.  
  335.         if (thisCallout->timeLeft > newCallout->timeLeft) {
  336.             thisCallout->timeLeft -= newCallout->timeLeft;
  337.  
  338.             /* 
  339.              * put it at the END of the (circular) list,
  340.              * which is to say, before thisCallout element.
  341.              */
  342.             listEnq(&(thisCallout->list), &(newCallout->list));
  343.             TRPRINT(TR_TEST, TR_LEVEL_1, ("Start() returns"));
  344.             Active->state = THREAD_ACTIVE;
  345.             return;
  346.         } 
  347.         newCallout->timeLeft -= thisCallout->timeLeft;
  348.  
  349.         SM_ASSERT(LEVEL_1, newCallout->timeLeft >= 0);
  350.     }
  351.     /*
  352.      * We went throught the entire list (or found the list null).
  353.      * This element goes on the end.
  354.      */
  355.     listEnq(&(calloutList), &(newCallout->list));
  356.  
  357.     TRPRINT(TR_TEST, TR_LEVEL_1, ("Start() returns"));
  358.     if(Active) 
  359.         Active->state = THREAD_ACTIVE;
  360.     return;
  361. }
  362.  
  363.  
  364. BOOL
  365. Timer:: Remove(
  366.     TIMERFUNC  func,
  367.     TIMERARG   arg
  368. )
  369. {
  370.     CALLOUT *thisCallout, *nextCallout;
  371.  
  372.     enterTIMER;
  373.  
  374.     TRPRINT(TR_TEST, TR_LEVEL_1, ("Remove( func 0x%x arg 0x%x)", func, arg));
  375.     nremoved++;
  376.  
  377.     for(
  378.         thisCallout = (CALLOUT *) FIRST_LIST_ELEMENT(&calloutList);
  379.         thisCallout != NULL;
  380.         thisCallout = (CALLOUT *)NEXT_LIST_ELEMENT( &(thisCallout->list) ) ) {
  381.  
  382.         if( thisCallout->func == func && thisCallout->arg == arg ) {
  383.             TRPRINT(TR_TEST, TR_LEVEL_1, 
  384.                 ("removed func 0x%x(arg 0x%x), had time %d", func, arg,
  385.                     thisCallout->timeLeft));
  386.  
  387.             nextCallout = (CALLOUT *)NEXT_LIST_ELEMENT(&(thisCallout->list));
  388.             if(nextCallout) {
  389.                 nextCallout->timeLeft += thisCallout->timeLeft;
  390.                 SM_ASSERT(LEVEL_1, nextCallout->timeLeft >= 0);
  391.             }
  392.  
  393.             ncallout--;
  394.             listMoveEnq(&freeList, &(thisCallout->list));
  395.             nfree ++;
  396.  
  397.             TRPRINT(TR_TEST, TR_LEVEL_1, ("Remove returns (found)"));
  398.  
  399.             Active->state = THREAD_ACTIVE;
  400.             return TRUE;
  401.         }
  402.     }
  403.     nnotfound++;
  404.     TRPRINT(TR_TEST, TR_LEVEL_1, ("Remove returns (not found)"));
  405.     Active->state = THREAD_ACTIVE;
  406.     return FALSE;
  407. }
  408.  
  409. void
  410. Timer:: PrintStats()
  411. {
  412.     if(nstarted>0) {
  413.         fprintf(stdout,"TIMERS:\t%2d%% usage highwater, %2d%% expiring, %2d%% free (now)\n", 
  414.             (100*highwaterUsed)/(ncallout+nfree+nexpired), 
  415.             (100*nexpired)/(ncallout+nfree+nexpired),
  416.             (100*nfree)/(ncallout+nfree+nexpired)  );
  417.         fprintf(stdout,"\t%8d ticks, %8d started\n", nticks, nstarted);
  418.         fprintf(stdout,"\t%8d called, %8d removed (%8d not found)\n", 
  419.             ncalled, nremoved, nnotfound);
  420.  
  421.     } else {
  422.         fprintf(stdout,"Timers:\tNo timers used yet.\n");
  423.     }
  424.  
  425.     
  426.     printStats();
  427. }
  428.  
  429. void
  430. Timer:: ClearStats()
  431. {
  432.     nticks = nstarted = ncalled = nremoved = nnotfound = 0;
  433.     /* cannot touch ncallout or nfree */
  434.     clearStats();
  435. }
  436.  
  437. #ifdef DEBUG
  438.  
  439. void
  440. Timer:: Dump(int line, char * file)
  441. {
  442.     CALLOUT        *thisCallout;
  443.     register    int            n;
  444.  
  445.     TRPRINT(TR_TEST, TR_LEVEL_1, ("DUMP from %20.20s line %d", file, line));
  446.  
  447.     for(
  448.         (n=0), thisCallout = (CALLOUT *) FIRST_LIST_ELEMENT(&calloutList);
  449.         thisCallout != NULL;
  450.         n++, thisCallout = (CALLOUT *)NEXT_LIST_ELEMENT( &(thisCallout->list))){
  451.  
  452.         TRPRINT(TR_TEST, TR_LEVEL_1,
  453.             ("%10d sec : #%d f=0x%x(a=0x%x)",
  454.                 thisCallout->timeLeft, thisCallout->cardinal, 
  455.                 thisCallout->func, thisCallout->arg));
  456.     }
  457.     if(n != ncallout) {
  458.         TRPRINT(TR_TEST, TR_LEVEL_1, ("ERROR: n %d, n %d", n, ncallout));
  459.     }
  460.     for(
  461.         (n=0), thisCallout = (CALLOUT *) FIRST_LIST_ELEMENT(&freeList);
  462.         thisCallout != NULL;
  463.         n++, thisCallout = (CALLOUT *)NEXT_LIST_ELEMENT( &(thisCallout->list))){
  464.     }
  465.     if(n != nfree) {
  466.         TRPRINT(TR_TEST, TR_LEVEL_1, ("ERROR: n %d, nfree %d", n, nfree));
  467.     }
  468.  
  469.     dump();
  470. }
  471.  
  472. void
  473. DumpTimers()
  474. {
  475.     ServerTimer.Dump(__LINE__, __FILE__);
  476. }
  477.  
  478. #endif DEBUG
  479.